pub mod des;
pub mod ser;

use crate::util::find_rvalue_kv;
use crate::{Error, Result};
use alloc::format;
use alloc::string::String;
use alloc::vec::Vec;
use rmpv::ValueRef as RValueRef;
use rmpv::{Utf8StringRef, Value as RValue};

#[derive(Debug, Clone)]
pub enum Value {
    Unknown,
    None,
    Bool(bool),
    I8(i8),
    U8(u8),
    U16(u16),
    I16(i16),
    I32(i32),
    U32(u32),
    I64(i64),
    U64(u64),
    F32(f32),
    F64(f64),
    Enum(String),
    Str(String),
    Array(Vec<u8>),
}

pub struct CommonValue {
    pub value_len: u32,
    pub value: Value,
}

pub struct NamedValue {
    pub name: String,
    pub value: CommonValue,
}

impl NamedValue {
    pub fn parse(val: &RValue) -> Result<Self> {
        let mut my = Self {
            name: String::from(""),
            value: CommonValue {
                value_len: 1,
                value: Value::None,
            },
        };
        if !val.is_map() {
            return Err(Error::InvalidParam);
        }
        let mut flags: u8 = 0;
        for (k, v) in val.as_map().unwrap() {
            if !k.is_str() {
                continue;
            }
            match k.as_str().unwrap() {
                "id" => {
                    if v.is_str() {
                        my.name = String::from(v.as_str().unwrap());
                        flags |= 0x1;
                    }
                }
                "vl" => {
                    if v.is_number() {
                        my.value.value_len = v.as_u64().unwrap() as u32;
                        flags |= 0x2;
                    }
                }
                _ => {
                    continue;
                }
            }
        }
        if flags & 0x3 != 0x3 {
            return Err(Error::Other(String::from("lost id or vl field")));
        }
        let vt = find_rvalue_kv(val, "vt");
        let v = find_rvalue_kv(val, "v");
        if v.is_none() || vt.is_none() || !vt.unwrap().is_number() {
            return Err(Error::Other(String::from(
                "lost v or vt field or invalid fields",
            )));
        }
        let vv = v.unwrap();
        let vtv = vt.unwrap().as_u64().unwrap() as u8;
        my.value.value = if vtv & 0x80 != 0 {
            if v.unwrap().is_bin() {
                Value::Array((Vec::from(v.unwrap().as_slice().unwrap())))
            } else {
                return Err(Error::Other(String::from("v not array")));
            }
        } else {
            match vtv & 0x7f {
                0 => Value::None,
                1 => {
                    Self::check(vv.is_bool(), "not bool")?;
                    Value::Bool(vv.as_bool().unwrap())
                }
                2 => {
                    Self::check(vv.is_number(), "not number")?;
                    Value::U8(vv.as_u64().unwrap() as u8)
                }
                3 => {
                    Self::check(vv.is_number(), "not number")?;
                    Value::U16(vv.as_u64().unwrap() as u16)
                }
                4 => {
                    Self::check(vv.is_number(), "not number")?;
                    Value::U32(vv.as_u64().unwrap() as u32)
                }
                5 => {
                    Self::check(vv.is_number(), "not number")?;
                    Value::U64(vv.as_u64().unwrap())
                }
                6 => {
                    Self::check(vv.is_number(), "not number")?;
                    Value::I8(vv.as_i64().unwrap() as i8)
                }
                7 => {
                    Self::check(vv.is_number(), "not number")?;
                    Value::I16(vv.as_i64().unwrap() as i16)
                }
                8 => {
                    Self::check(vv.is_number(), "not number")?;
                    Value::I32(vv.as_i64().unwrap() as i32)
                }
                9 => {
                    Self::check(vv.is_number(), "not number")?;
                    Value::I64(vv.as_i64().unwrap())
                }
                10 => {
                    Self::check(vv.is_f32(), "not float")?;
                    Value::F32(vv.as_f64().unwrap() as f32)
                }
                11 => {
                    Self::check(vv.is_f64(), "not double")?;
                    Value::F64(vv.as_f64().unwrap())
                }
                12 | 13 => {
                    Self::check(vv.is_str(), "not str")?;
                    Value::Str(String::from(vv.as_str().unwrap()))
                }
                others => {
                    return Err(Error::Other(format!("vt {} not support", others)));
                }
            }
        };
        Ok(my)
    }

    fn check(cond: bool, info: &str) -> Result<()> {
        if !cond {
            Err(Error::Other(format!("v not {}", info)))
        } else {
            Ok(())
        }
    }

    pub fn into_rvalue_ref<'a>(&self) -> RValueRef<'_> {
        let mut val = Vec::new();
        val.push((
            RValueRef::String(Utf8StringRef::from("id")),
            RValueRef::String(Utf8StringRef::from(self.name.as_str())),
        ));
        let (v, vt, vl) = match self.value.value {
            Value::None => (RValueRef::Nil, 0u8, 1u32),
            Value::Bool(v) => (RValueRef::Boolean(v), 1, 1),
            Value::U8(v) => (RValueRef::Integer(v.into()), 2, 1),
            Value::I8(v) => (RValueRef::Integer(v.into()), 6, 1),
            Value::U16(v) => (RValueRef::Integer(v.into()), 3, 2),
            Value::I16(v) => (RValueRef::Integer(v.into()), 7, 2),
            Value::U32(v) => (RValueRef::Integer(v.into()), 4, 4),
            Value::I32(v) => (RValueRef::Integer(v.into()), 8, 4),
            Value::U64(v) => (RValueRef::Integer(v.into()), 5, 8),
            Value::I64(v) => (RValueRef::Integer(v.into()), 9, 8),
            Value::F32(v) => (RValueRef::F32(v), 10, 4),
            Value::F64(v) => (RValueRef::F64(v), 11, 8),
            Value::Str(ref v) => (
                RValueRef::String(Utf8StringRef::from(v.as_str())),
                12,
                v.len() as u32,
            ),
            Value::Enum(ref v) => (
                RValueRef::String(Utf8StringRef::from(v.as_str())),
                13,
                v.len() as u32,
            ),
            Value::Array(ref v) => (RValueRef::Binary(v.as_slice()), 0x80, v.len() as u32),
            Value::Unknown => panic!("unknown value"),
        };
        val.push((RValueRef::String(Utf8StringRef::from("v")), v));
        val.push((
            RValueRef::String(Utf8StringRef::from("vl")),
            RValueRef::Integer(vl.into()),
        ));
        val.push((
            RValueRef::String(Utf8StringRef::from("vt")),
            RValueRef::Integer(vt.into()),
        ));
        RValueRef::Map(val)
    }
}

pub struct Prop {
    pub name: String,
    pub features: Vec<NamedValue>,
}

pub type CallArg = NamedValue;
pub type PropFeature = NamedValue;

pub struct Event {
    pub name: String,
    pub value: CommonValue,
}

pub struct Alarm {
    pub name: String,
    pub level: u8,
    pub desc: String,
}
